﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Glimpse.AspNet;
using Glimpse.Core.Extensibility;
using Glimpse.Core.Framework;
using Orchard;
using Orchard.Environment;
using Orchard.Mvc;

namespace Glimpse
{
    public class Configurator : IWorkContextEvents, ISingletonDependency
    {
        private readonly IHttpContextAccessor _hca;
        private bool _isInitialized = false;

        private const string RuntimeKey = "__GlimpseRuntime";
        private const string LoggerKey = "__GlimpseLogger";
        private static readonly object LockObj = new object();
        private static readonly Factory Factory;


        static Configurator()
        {
            Factory = new Factory(new AspNetServiceLocator());
        }

        public Configurator(IHttpContextAccessor hca)
        {
            _hca = hca;
        }


        public void Started()
        {
            var httpContext = _hca.Current();
            var runtime = GetRuntime(httpContext.Application);

            if (!runtime.IsInitialized)
            {
                AppDomain.CurrentDomain.SetData(LoggerKey, Factory.InstantiateLogger());
                runtime.Initialize();

                _isInitialized = true;
            }


            if (!runtime.IsInitialized) return;

            BeginRequest(httpContext);
            BeginSessionAccess(httpContext);

            //if (runtime.IsInitialized || runtime.Initialize())
            //{
            //    httpApplication.BeginRequest += (context, e) => BeginRequest(WithTestable(context));
            //    httpApplication.PostAcquireRequestState += (context, e) => BeginSessionAccess(WithTestable(context));
            //    httpApplication.PostRequestHandlerExecute += (context, e) => EndSessionAccess(WithTestable(context));
            //    httpApplication.PostReleaseRequestState += (context, e) => EndRequest(WithTestable(context));
            //}
        }

        public void Finished()
        {
            var httpContext = _hca.Current();

            if (!GetRuntime(httpContext.Application).IsInitialized) return;

            EndSessionAccess(httpContext);
            EndRequest(httpContext);
        }


        internal IGlimpseRuntime GetRuntime(HttpApplicationStateBase applicationState)
        {
            var runtime = applicationState[RuntimeKey] as IGlimpseRuntime;

            if (runtime == null)
            {
                lock (LockObj)
                {
                    runtime = applicationState[RuntimeKey] as IGlimpseRuntime;

                    if (runtime == null)
                    {
                        // HACK: Due to a current bug in Factory, I'm just overriding the config here before creating the runtime.
                        var config = Factory.InstantiateConfiguration() as GlimpseConfiguration;
                        config.EndpointBaseUri = "~/Glimpse";
                        config.DefaultRuntimePolicy = RuntimePolicy.On;

                        runtime = new GlimpseRuntime(config);

                        applicationState.Add(RuntimeKey, runtime);
                    }
                }
            }

            return runtime;
        }

        internal void BeginRequest(HttpContextBase httpContext)
        {
            // TODO: Add Logging to either methods here or in Runtime
            var runtime = GetRuntime(httpContext.Application);

            runtime.BeginRequest();
        }

        internal void EndRequest(HttpContextBase httpContext)
        {
            var runtime = GetRuntime(httpContext.Application);

            runtime.EndRequest();
        }

        private static HttpApplicationBase WithTestable(HttpApplication httpApplication)
        {
            return new HttpApplicationWrapper(httpApplication);
        }

        private void BeginSessionAccess(HttpContextBase httpContext)
        {
            var runtime = GetRuntime(httpContext.Application);

            runtime.BeginSessionAccess();
        }

        private void EndSessionAccess(HttpContextBase httpContext)
        {
            var runtime = GetRuntime(httpContext.Application);

            runtime.EndSessionAccess();
        }
    }
}